import type { TypedKey } from './types';

export class Context {
  protected data: Record<string, unknown>;
  protected listeners: Record<string, Array<(newValue: unknown) => void>> = {};
  protected batchUpdates: Map<string, unknown> = new Map();
  protected batchTimer: number | null = null;

  constructor(initialValue: Record<string, unknown> = {}) {
    this.data = { ...initialValue };
  }

  getData<T>(key: TypedKey<T>): T {
    if (!(key.key in this.data)) {
      this.data[key.key] = key.defaultValue;
    }
    return this.data[key.key] as T;
  }

  setData<T>(key: TypedKey<T>, value: T, immediate = true): void {
    if (immediate) {
      this.data[key.key] = value;
      this.notifyListeners(key.key, value);
    } else {
      this.batchUpdates.set(key.key, value);

      if (!this.batchTimer) {
        this.batchTimer = setTimeout(() => {
          this.flushBatchUpdates();
        }, 0);
      }
    }
  }

  subscribe<T>(key: TypedKey<T>, listener: (newValue: T) => void): () => void {
    const keyStr = key.key;
    if (!this.listeners[keyStr]) {
      this.listeners[keyStr] = [];
    }
    this.listeners[keyStr].push(listener as (newValue: unknown) => void);
    return () => {
      this.listeners[keyStr] = this.listeners[keyStr].filter(
        (l) => l !== listener,
      );
    };
  }

  batch(callback: () => void): void {
    this.flushBatchUpdates();
    callback();
    this.flushBatchUpdates();
  }

  protected flushBatchUpdates(): void {
    this.batchUpdates.forEach((value, key) => {
      this.data[key] = value;
      this.notifyListeners(key, value);
    });
    this.batchUpdates.clear();

    if (this.batchTimer) {
      clearTimeout(this.batchTimer);
      this.batchTimer = null;
    }
  }

  protected notifyListeners(key: string, newValue: unknown): void {
    if (this.listeners[key]) {
      // 使用切片复制数组，避免在遍历过程中修改数组
      this.listeners[key].slice().forEach((listener) => {
        try {
          listener(newValue);
        } catch (error) {
          console.error(`Error in listener for key ${key}:`, error);
        }
      });
    }
  }

  // 工具方法
  getAllData(): Record<string, unknown> {
    return { ...this.data };
  }

  clear(): void {
    this.data = {};
    this.batchUpdates.clear();
    Object.keys(this.listeners).forEach((key) => {
      this.listeners[key] = [];
    });
  }
}
